#ifndef FLASH_H_
#define FLASH_H_

#include <stdint.h>

/* Common parameters -- kind of high, but they should only occur when there
 * is a problem (and well your system already is broken), so err on the side
 * of caution in case we're dealing with slower SPI buses and/or processors.
 */
#define FLASH_PROG_TIMEOUT              (2000)
#define FLASH_SECTOR_ERASE_TIMEOUT      (5000)

#define FLASH_CMD_CFI                   0x98
#define FLASH_CMD_READ_ID               0x90

#define AMD_CMD_RESET                   0xF0
#define AMD_CMD_WRITE                   0xA0
#define AMD_CMD_ERASE_START             0x80
#define AMD_CMD_ERASE_BLOCK             0x30
#define AMD_CMD_ERASE_CHIP              0x10
#define AMD_CMD_UNLOCK_START            0xAA
#define AMD_CMD_UNLOCK_ACK              0x55
#define AMD_CMD_UNLOCK_BYPASS           0x20
#define AMD_CMD_UNLOCK_BYPASS_RESET1    0x90
#define AMD_CMD_UNLOCK_BYPASS_RESET2    0x00


/*
 * NAND Flash Manufacturer ID Codes
 */
#define NAND_MFR_TOSHIBA                0x98
#define NAND_MFR_SAMSUNG                0xec
#define NAND_MFR_FUJITSU                0x04
#define NAND_MFR_NATIONAL               0x8f
#define NAND_MFR_RENESAS                0x07
#define NAND_MFR_STMICRO                0x20
#define NAND_MFR_HYNIX                  0xad
#define NAND_MFR_MICRON                 0x2c
#define NAND_MFR_AMD                    0x01

/*
 * Standard NAND flash commands
 */
#define NAND_CMD_READ0                  0
#define NAND_CMD_READ1                  1
#define NAND_CMD_RNDOUT                 5
#define NAND_CMD_PAGEPROG               0x10
#define NAND_CMD_READOOB                0x50
#define NAND_CMD_ERASE1                 0x60
#define NAND_CMD_STATUS                 0x70
#define NAND_CMD_STATUS_MULTI           0x71
#define NAND_CMD_SEQIN                  0x80
#define NAND_CMD_RNDIN                  0x85
#define NAND_CMD_READID                 0x90
#define NAND_CMD_PARAM                  0xec
#define NAND_CMD_ERASE2                 0xd0
#define NAND_CMD_RESET                  0xff

/* Extended commands for large page devices */
#define NAND_CMD_READSTART              0x30
#define NAND_CMD_RNDOUTSTART            0xE0
#define NAND_CMD_CACHEDPROG             0x15

/* Extended commands for AG-AND device */
/*
 * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
 *       there is no way to distinguish that from NAND_CMD_READ0
 *       until the remaining sequence of commands has been completed
 *       so add a high order bit and mask it off in the command.
 */
#define NAND_CMD_DEPLETE1               0x100
#define NAND_CMD_DEPLETE2               0x38
#define NAND_CMD_STATUS_MULTI           0x71
#define NAND_CMD_STATUS_ERROR           0x72
/* multi-bank error status (banks 0-3) */
#define NAND_CMD_STATUS_ERROR0          0x73
#define NAND_CMD_STATUS_ERROR1          0x74
#define NAND_CMD_STATUS_ERROR2          0x75
#define NAND_CMD_STATUS_ERROR3          0x76
#define NAND_CMD_STATUS_RESET           0x7f
#define NAND_CMD_STATUS_CLEAR           0xff

#define NAND_CMD_NONE                   -1

/* Status bits */
#define NAND_STATUS_FAIL                0x01
#define NAND_STATUS_FAIL_N1             0x02
#define NAND_STATUS_TRUE_READY          0x20
#define NAND_STATUS_READY               0x40
#define NAND_STATUS_WP                  0x80

#define NAND_BAD_MARK                   0x44

#define NAND_ECC_WR     1
#define NAND_ECC_RD     0
#if 0
typedef struct _ecc_t {
        uint32_t size;
        uint32_t steps;
        uint32_t bytes;
        uint32_t ecctotal;
        const uint8_t * eccpos;
        uint8_t  * ecccalc;
        uint8_t  * eccread;
        uint8_t  * eccoob;
        void    (*hwctl)(int mode);
        int     (*calculate)(const uint8_t *dat, uint8_t *ecc_code);
        int     (*correct)(uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc);
}ecc_t;
#else
typedef struct _ecc_t {
        uint32_t size;
        uint32_t steps;
        uint32_t bytes;
        uint32_t ecctotal;
        uint8_t  eccpos[64];
        uint8_t   ecccalc[64];
        uint8_t   eccread[64];
        uint8_t   eccoob[64];
        void    (*hwctl)(int mode);
        int     (*calculate)(const uint8_t *dat, uint8_t *ecc_code);
        int     (*correct)(uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc);
}ecc_t;


#endif
int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code);
int nand_correct_data(uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc);

/******************** Flash Device Drivers ********************/
#include "dev.h"

typedef struct _flash_t flash_t;

typedef struct {
        ecc_t * ecc;
        uint32_t bus_addr;
        uint32_t CLE_bit;
        uint32_t ALE_bit;
        uint32_t RB_gpio;
}nand_base_t;

/*
 * Flash Device Operation
 * Manufacture based
 */
typedef struct {
        const char * name;
        int (*probe)(flash_t * fl);
        int (*read)(flash_t * fl, uint32_t offset, void *buf, size_t len);
        int (*write)(flash_t * fl, uint32_t offset, const void *buf, size_t len);
        int (*erase)(flash_t * fl, uint32_t offset, size_t len);
}flashdrv_t;

struct _flash_t{
        union {
                spi_slave_t * spi;  /* SPI Flash */
                uint32_t nor_addr;  /* NOR Flash */
                nand_base_t bus_nand; /* NAND Flash in bus */
        }inf;
        flashdrv_t * drv;
        void * priv;
};

int flash_probe(const char * name, flash_t *fl);

#define PART_PHY_NOR        0x0001
#define PART_PHY_SPI        0x0002
#define PART_PHY_NAND       0x0004
#define PART_PHY_SRAM       0x0008
#define PART_PHY_SOC        0x0010

#define PART_TYPE_SIG       0x8000
#define PART_TYPE_MOD       0x0100
#define PART_TYPE_RES       0x0200
#define PART_TYPE_FS        0x0400
#define PART_TYPE_SYS       0x0800

#define PART_TYPE_LIB       0x9100
#define PART_TYPE_APP       0xA100
typedef struct {
        char * name;
        uint32_t offset;
        uint32_t size;
        uint32_t flags;
        flash_t * flash;
}part_t;

int register_parts(part_t * parts, uint32_t nr);
part_t * get_part_by_name(char * name, uint32_t * index);
part_t * get_part_by_index(uint32_t index);
int part_erase(part_t * part, uint32_t offset, size_t len);
int part_read(part_t * part, uint32_t offset, void *buf, size_t len);
int part_write(part_t * part, uint32_t offset, const void *buf, size_t len);
uint32_t part_base(part_t * part);
#endif /* FLASH_H_ */
